package com.zrq.secondkill.service.impl;

import com.zrq.secondkill.constant.Constant;
import com.zrq.secondkill.dao.ItemStockDOMapper;
import com.zrq.secondkill.dao.OrderDOMapper;
import com.zrq.secondkill.dao.SequenceDOMapper;
import com.zrq.secondkill.dao.StockLogDOMapper;
import com.zrq.secondkill.dataobject.OrderDO;
import com.zrq.secondkill.enums.OrderStatusEnum;
import com.zrq.secondkill.enums.PromoStatusEnum;
import com.zrq.secondkill.error.BusinessErrorEnum;
import com.zrq.secondkill.error.BusinessException;
import com.zrq.secondkill.mq.MqProducer;
import com.zrq.secondkill.service.ItemService;
import com.zrq.secondkill.service.OrderService;
import com.zrq.secondkill.service.UniqueIdService;
import com.zrq.secondkill.service.UserService;
import com.zrq.secondkill.service.model.ItemModel;
import com.zrq.secondkill.service.model.OrderModel;
import com.zrq.secondkill.service.model.UserModel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;

@Slf4j
@Service
public class OrderServiceImpl implements OrderService {

    private static final Integer MAX_BUY_NUM = 99;

    final
    ItemService itemService;

    final
    UserService userService;

    final
    SequenceDOMapper sequenceDOMapper;

    final
    OrderDOMapper orderDOMapper;

    final
    ItemStockDOMapper itemStockDOMapper;

    final
    StockLogDOMapper stockLogDOMapper;

    final
    UniqueIdService uniqueIdService;

    final
    RedisTemplate redisTemplate;

    @Autowired
    private MqProducer mqProducer;

    public OrderServiceImpl(ItemService itemService, UserService userService, SequenceDOMapper sequenceDOMapper, OrderDOMapper orderDOMapper, ItemStockDOMapper itemStockDOMapper, StockLogDOMapper stockLogDOMapper, UniqueIdService uniqueIdService, RedisTemplate redisTemplate) {
        this.itemService = itemService;
        this.userService = userService;
        this.sequenceDOMapper = sequenceDOMapper;
        this.orderDOMapper = orderDOMapper;
        this.itemStockDOMapper = itemStockDOMapper;
        this.stockLogDOMapper = stockLogDOMapper;
        this.uniqueIdService = uniqueIdService;
        this.redisTemplate = redisTemplate;
    }

    @Override
    @Transactional(rollbackFor = {Exception.class, RuntimeException.class})
    public void decreaseRedisStockAndWriteStockLog(Integer userId, Integer itemId, Integer amount, Integer promoId, String stockLogId) throws BusinessException {
        //校验商品信息
        ItemModel itemModel = itemService.getItemByIdInCache(itemId);
//        if (itemModel == null) {
//            throw new BusinessException(BusinessErrorEnum.PARAMETER_VALIDATION_ERROR, "商品不存在");
//        }

        //校验购买数量
        if (amount <= 0 || amount > MAX_BUY_NUM) {
            throw new BusinessException(BusinessErrorEnum.PARAMETER_VALIDATION_ERROR, "购买数量不合法");
        }

//        //校验用户信息
//        UserModel userModel = userService.getUserByIdInCache(userId);
//        if (userModel == null) {
//            throw new BusinessException(BusinessErrorEnum.PARAMETER_VALIDATION_ERROR, "用户信息不存在");
//        }

//        //校验促销信息
//        if (promoId != null) {
//            if (!promoId.equals(itemModel.getPromoModel().getId())) {
//                throw new BusinessException(BusinessErrorEnum.PARAMETER_VALIDATION_ERROR, "商品与促销活动不匹配");
//            }
//            if (itemModel.getPromoModel().getStatus() != PromoStatusEnum.ON_GOING) {
//                throw new BusinessException(BusinessErrorEnum.PARAMETER_VALIDATION_ERROR, "不在活动时间内");
//            }
//        }

        //写入订单
        OrderModel orderModel = new OrderModel();
        orderModel.setUserId(userId);
        orderModel.setItemId(itemId);
        orderModel.setAmount(amount);
        if (promoId != null) {
            orderModel.setPromoId(promoId);
            orderModel.setItemPrice(itemModel.getPromoModel().getPromoItemPrice());
        } else {
            orderModel.setItemPrice(itemModel.getPrice());
        }

        orderModel.setId(uniqueIdService.generateUniqueId());

        orderModel.setOrderPrice(orderModel.getItemPrice().multiply(new BigDecimal(amount)));
        orderDOMapper.insert(convertOrderDOFromOrderModel(orderModel));


        //TODO(zrq): 若后续数据库操作失败回滚,此处redis扣减库存不会自动回滚,会导致少卖
        //尝试减库存
        Boolean decreaseResult = itemService.decreaseStockInRedisIfEnough(itemId, amount);
        if (!decreaseResult) {
            throw new BusinessException(BusinessErrorEnum.STOCK_NOT_ENOUGH);
        }

        //TODO(zrq): 如果此处数据库读写成为瓶颈,可以取消流水的预创建,若成功则创建流水,返回stockLogId,可将此处修改状态避免,仅需一次插入
        //扣减库存成功,修改stockLog状态
        //此处需要读写数据库,但将竞争行锁转化为了无锁竞争的一次使用主键索引的修改
        try {
            stockLogDOMapper.updateStatusByPrimaryKey(OrderStatusEnum.SUCCESS.getStatus(), stockLogId);
        } catch (Exception e) {
            BusinessException businessException = new BusinessException(BusinessErrorEnum.DATABASE_OPERATION_FAIL, "修改stockLog失败");
            businessException.initCause(e);
            throw businessException;
        }
    }

    /**
     * 创建订单
     *
     * @return 是否成功
     */
    @Override
    public Boolean asyncCreateOrder(Integer userId, Integer itemId, Integer amount, Integer promoId) {

        String stockLogId = itemService.initStockLog(itemId, amount);

        return mqProducer.transactionalAsyncCreateOrder(userId, itemId, promoId, amount, stockLogId);
    }

    private static OrderDO convertOrderDOFromOrderModel(OrderModel orderModel) {
        OrderDO orderDO = new OrderDO();
        BeanUtils.copyProperties(orderModel, orderDO);
        orderDO.setOrderPrice(orderModel.getOrderPrice().doubleValue());
        orderDO.setItemPrice(orderModel.getItemPrice().doubleValue());
        return orderDO;
    }

}
